/**
 * Copyright 2012-2013 University Of Southern California
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package org.workflowsim.examples;

import java.io.File;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import org.cloudbus.cloudsim.Cloudlet;
import org.cloudbus.cloudsim.CloudletSchedulerSpaceShared;
import org.cloudbus.cloudsim.DatacenterCharacteristics;
import org.cloudbus.cloudsim.HarddriveStorage;
import org.cloudbus.cloudsim.Host;
import org.cloudbus.cloudsim.Log;
import org.cloudbus.cloudsim.Pe;
import org.cloudbus.cloudsim.Storage;
import org.cloudbus.cloudsim.VmAllocationPolicySimple;
import org.cloudbus.cloudsim.VmSchedulerTimeShared;
import org.cloudbus.cloudsim.core.CloudSim;
import org.cloudbus.cloudsim.provisioners.BwProvisionerSimple;
import org.cloudbus.cloudsim.provisioners.PeProvisionerSimple;
import org.cloudbus.cloudsim.provisioners.RamProvisionerSimple;
import org.workflowsim.CondorVM;
import org.workflowsim.Task;
import org.workflowsim.WorkflowDatacenter;
import org.workflowsim.Job;
import org.workflowsim.WorkflowEngine;
import org.workflowsim.WorkflowPlanner;
import org.workflowsim.utils.ClusteringParameters;
import org.workflowsim.utils.OverheadParameters;
import org.workflowsim.utils.Parameters;
import org.workflowsim.utils.ReplicaCatalog;
import org.workflowsim.utils.Parameters.ClassType;

/**
 * This WorkflowSimExample creates a workflow planner, a workflow engine, and
 * one schedulers, one data centers and 20 vms. You should change daxPath at
 * least. You may change other parameters as well.
 *这个WorkflowSimExample创建一个工作流规划程序,工作流引擎, 
 *一个调度器,一个数据中心和20个vm。你应该改变daxPath 
 *你可以改变其他参数。
 * @author Weiwei Chen
 * @since WorkflowSim Toolkit 1.0
 * @date Apr 9, 2013
 */
public class WorkflowSimBasicExample1 {
	
	/**
	 * Creates a container to store VMs. This list is passed to the broker later
	 * 创建一个容器来存储虚拟机。这个列表传递给代理
	 * @param userId 用户ID
	 * @param vms 虚拟机数量
	 * @return List 返回一个虚拟机列表
	 */
    protected static List<CondorVM> createVM(int userId, int vms) {
        
        LinkedList<CondorVM> list = new LinkedList<>();

        //VM Parameters
		//虚拟机参数
        long size = 10000; //image size (MB)存储空间大小
        int ram = 512; //vm memory (MB)虚拟机内存大小
        int mips = 1000;//CPU运算速度
        long bw = 1000;//带宽
        int pesNumber = 1; //number of cpus CPU的数目
        String vmm = "Xen"; //VMM name虚拟机管理程序的名字

        //create VMs
		//创建虚拟机
        CondorVM[] vm = new CondorVM[vms];
        for (int i = 0; i < vms; i++) {
            double ratio = 1.0;
                      //CondorVM(虚拟机ID，用户ID，CPU速度 ，CPU数目，内存大小，带宽，虚拟机管理程序，空间共享)
            vm[i] = new CondorVM(i, userId, mips * ratio, pesNumber, ram, bw, size, vmm, new CloudletSchedulerSpaceShared());
            list.add(vm[i]);
        }
        return list;
    }

    ////////////////////////// STATIC METHODS 静态方法///////////////////////
    /**
     * Creates main() to run this example This example has only one datacenter
     * and one storage
	 * 创建一个主函数来运行这个例子，这个例子只有一个数据中心和一个storage
     */
    public static void main(String[] args) {
        try {
            // First step: Initialize the WorkflowSim package. 
			//初始化WorkflowSim包
            /**
             * However, the exact number of vms may not necessarily be vmNum If
             * the data center or the host doesn't have sufficient resources the
             * exact vmNum would be smaller than that. Take care.
			 *然而,虚拟机的具体数量可能不一定是vmNum如果 
			 *数据中心或主机没有足够的资源 
			 *具体的虚拟机数量将小于vmNum。注意。
             */
            int vmNum = 20;//number of vms;虚拟机数量
            /**
             * Should change this based on real physical path
			 * 应该根据真实的物理路径改变这个路径
             */
            //String daxPath = "/Users/weiweich/NetBeansProjects/WorkflowSim-1.0/config/dax/Montage_100.xml";//原文件的路径
			//这个文件所在的路径
	//	String daxPath = "D:/eclipseroom/Cloudsim/WorkflowSim-1.0-master/src/org/workflowsim/examples/WorkflowSimBasicExample1.java";
          // String daxPath = "D:/eclipseroom/Cloudsim/WorkflowSim-1.0-master/config/dax/Montage_100.xml";
            String daxPath = "config/dax/Montage_100.xml";
			/**
			 * 关于daxPath路径的问题,才有这个类所在的路径得出的结果不正确，采用相对路径和.xml的绝对路径的出的结果是一致的
			 * 因此不要采用类的路径
			 */
            File daxFile = new File(daxPath);
            if (!daxFile.exists()) {
                Log.printLine("Warning: Please replace daxPath with the physical path in your working environment!");
                return;
            }

            /**
             * Since we are using MINMIN scheduling algorithm, the planning
             * algorithm should be INVALID such that the planner would not
             * override the result of the scheduler
			 * 因为我们使用的是MIN-MIN调度算法,规划 
			 * 算法应该无效,规划师不会 
			 * 覆盖调度程序的结果
             */
            Parameters.SchedulingAlgorithm sch_method = Parameters.SchedulingAlgorithm.MINMIN;
            Parameters.PlanningAlgorithm pln_method = Parameters.PlanningAlgorithm.INVALID;
            ReplicaCatalog.FileSystem file_system = ReplicaCatalog.FileSystem.SHARED;

            /**
             * No overheads
			 * 无开销
             */
            OverheadParameters op = new OverheadParameters(0, null, null, null, null, 0);

            /**
             * No Clustering
			 * 无集群
             */
            ClusteringParameters.ClusteringMethod method = ClusteringParameters.ClusteringMethod.NONE;
            ClusteringParameters cp = new ClusteringParameters(0, 0, method, null);

            /**
             * Initialize static parameters
			 * 初始化静态参数
             */
            Parameters.init(vmNum, daxPath, null,
                    null, op, cp, sch_method, pln_method,
                    null, 0);
            ReplicaCatalog.init(file_system);

            // before creating any entities在创建任何实体之前.
            int num_user = 1;   // number of grid users网格用户的数量
            Calendar calendar = Calendar.getInstance();
            boolean trace_flag = false;  // mean trace events意思是跟踪事件

            // Initialize the CloudSim library初始化CloudSim库
            CloudSim.init(num_user, calendar, trace_flag);

            WorkflowDatacenter datacenter0 = createDatacenter("Datacenter_0");

            /**
             * Create a WorkflowPlanner with one schedulers.
			 * 创建一个拥有一个调度程序的工作流规划程序
             */
            WorkflowPlanner wfPlanner = new WorkflowPlanner("planner_0", 1);
            /**
             * Create a WorkflowEngine.
			 * 创建一个工作流引擎
             */
            WorkflowEngine wfEngine = wfPlanner.getWorkflowEngine();
            /**
             * Create a list of VMs.The userId of a vm is basically the id of
             * the scheduler that controls this vm.
			 * 创建一个虚拟机列表。一个vm的userId是基本的id这个虚拟机调度程序控制。
             */
            List<CondorVM> vmlist0 = createVM(wfEngine.getSchedulerId(0), Parameters.getVmNum());

            /**
             * Submits this list of vms to this WorkflowEngine.
			 * 提交这个虚拟机列表给工作流引擎
             */
            wfEngine.submitVmList(vmlist0, 0);

            /**
             * Binds the data centers with the scheduler.
			 * 绑定的数据中心调度器
             */
            wfEngine.bindSchedulerDatacenter(datacenter0.getId(), 0);
            CloudSim.startSimulation();
            List<Job> outputList0 = wfEngine.getJobsReceivedList();
            CloudSim.stopSimulation();
            printJobList(outputList0);
        } catch (Exception e) {
            Log.printLine("The simulation has been terminated due to an unexpected error");
        }
    }
    /**
     * 创建数据中心
     * @param name 数据中心的名字
     * @return WorkflowDatacenter 返回一个Workflow数据中心对象
     */
    protected static WorkflowDatacenter createDatacenter(String name) {

        // Here are the steps needed to create a PowerDatacenter:下面是创建PowerDatacenter所需的步骤
        // 1. We need to create a list to store one or more
        //    Machines我们需要创建一个表来存储一个或多个机器
        List<Host> hostList = new ArrayList<>();

        // 2. A Machine contains one or more PEs or CPUs/Cores. Therefore, should
        //    create a list to store these PEs before creating
        //    a Machine.一台机器包含一个或多个PEs或cpu核心。因此,应该创建一个表来存储这些PEs之前创建一台机器。
        for (int i = 1; i <= 20; i++) {
            List<Pe> peList1 = new ArrayList<>();
            int mips = 2000;
            // 3. Create PEs and add these into the list.创建PEs和这些添加到列表中
            //for a quad-core machine, a list of 4 PEs is required:四核机,4 PEs的列表是必需的
            peList1.add(new Pe(0, new PeProvisionerSimple(mips))); // need to store Pe id and MIPS Rating需要存储Pe id和MIPS评级
            peList1.add(new Pe(1, new PeProvisionerSimple(mips)));

            int hostId = 0;
            int ram = 2048; //host memory (MB)主机内存
            long storage = 1000000; //host storage主机存储空间大小
            int bw = 10000;
            hostList.add(
                    new Host(
                            hostId,
                            new RamProvisionerSimple(ram),
                            new BwProvisionerSimple(bw),
                            storage,
                            peList1,
                            new VmSchedulerTimeShared(peList1))); // This is our first machine这是我们第一个机器
            //hostId++;
        }

        // 4. Create a DatacenterCharacteristics object that stores the
        //    properties of a data center: architecture, OS, list of
        //    Machines, allocation policy: time- or space-shared, time zone
        //    and its price (G$/Pe time unit).
		//创建一个存储DatacenterCharacteristics（数据中心特征值）对象 
		//属性的数据中心:架构,操作系统,列表 
		//机器,分配策略:基于时间或共享空间,时区 
		//和价格()。
        String arch = "x86";              // system architecture系统架构
        String os = "Linux";             // operating system操作系统
        String vmm = "Xen";
        double time_zone = 10.0;         // time zone this resource located这个地区的时区
        double cost = 3.0;              // the cost of using processing in this resource这个资源的处理开销
        double costPerMem = 0.05;		// the cost of using memory in this resource这个资源的内存开销
        double costPerStorage = 0.1;	// the cost of using storage in this resource这个资源的存储开销
        double costPerBw = 0.1;			// the cost of using bw in this resource这个资源的带宽开销
        LinkedList<Storage> storageList = new LinkedList<>();	//we are not adding SAN devices by now我们不添加SAN（存储区域网络）设备了
        WorkflowDatacenter datacenter = null;

        DatacenterCharacteristics characteristics = new DatacenterCharacteristics(
                arch, os, vmm, hostList, time_zone, cost, costPerMem, costPerStorage, costPerBw);

        // 5. Finally, we need to create a storage object.最后,我们需要创建一个存储对象。
        /**
         * The bandwidth within a data center in MB/s.在数据中心带宽MB/s
         */
        int maxTransferRate = 15;// the number comes from the futuregrid site, you can specify your bw
		//来自futuregrid站点数量,你可以指定bw
        try {
            // Here we set the bandwidth to be 15MB/s在这里我们设置带宽15 mb / s
            HarddriveStorage s1 = new HarddriveStorage(name, 1e12);
            s1.setMaxTransferRate(maxTransferRate);
            storageList.add(s1);
            datacenter = new WorkflowDatacenter(name, characteristics, new VmAllocationPolicySimple(hostList), storageList, 0);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return datacenter;
    }

    /**
     * Prints the job objects
     * 打印工作对象
     * @param list list of jobs
     */
    protected static void printJobList(List<Job> list) {
        String indent = "    ";
        Log.printLine();
        Log.printLine("========== OUTPUT ==========");
        Log.printLine("Job ID" + indent + "Task ID" + indent + "STATUS" + indent
                + "Data center ID" + indent + "VM ID" + indent + indent
                + "Time" + indent + "Start Time" + indent + "Finish Time" + indent + "Depth");
        DecimalFormat dft = new DecimalFormat("###.##");
        for (Job job : list) {
            Log.print(indent + job.getCloudletId() + indent + indent);
            if (job.getClassType() == ClassType.STAGE_IN.value) {
                Log.print("Stage-in");
            }
            for (Task task : job.getTaskList()) {
                Log.print(task.getCloudletId() + ",");
            }
            Log.print(indent);

            if (job.getCloudletStatus() == Cloudlet.SUCCESS) {
                Log.print("SUCCESS");
                Log.printLine(indent + indent + job.getResourceId() + indent + indent + indent + job.getVmId()
                        + indent + indent + indent + dft.format(job.getActualCPUTime())
                        + indent + indent + dft.format(job.getExecStartTime()) + indent + indent + indent
                        + dft.format(job.getFinishTime()) + indent + indent + indent + job.getDepth());
            } else if (job.getCloudletStatus() == Cloudlet.FAILED) {
                Log.print("FAILED");
                Log.printLine(indent + indent + job.getResourceId() + indent + indent + indent + job.getVmId()
                        + indent + indent + indent + dft.format(job.getActualCPUTime())
                        + indent + indent + dft.format(job.getExecStartTime()) + indent + indent + indent
                        + dft.format(job.getFinishTime()) + indent + indent + indent + job.getDepth());
            }
        }
    }
}
